package com.xuexiang.xui_lib.component.progress.materialprogressbar;


import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.LinearShader;
import ohos.agp.render.Paint;
import ohos.agp.render.Shader;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

/**
 * 自定义横向ProgressBar
 *
 * @since 2021-05-08
 */
public class MaterialHorizontalProgress extends MaterialProgressBar {
    /**
     * the type of animation
     */
    private int mAnimateType = 0;
    /**
     * the progress of start point
     */
    private float mStartProgress = 0;
    /**
     * the progress of end point
     */
    //private float mEndProgress = 60;
    /**
     * the color of start progress
     */
    private Color mStartColor;
    /**
     * the color of end progress
     */
    private Color mEndColor;
    /**
     * has track of moving or not
     */
    private boolean trackEnabled = false;
    /**
     * the stroke width of progress
     */
    private int mTrackWidth = 6;
    /**
     * the size of inner text
     */
    private int mProgressTextSize = 48;
    /**
     * the color of inner text
     */
    private Color mProgressTextColor;
    /**
     * the color of progress track
     */
    private Color mTrackColor;
    /**
     * the duration of progress moving
     */
    private int mProgressDuration = 1200;
    /**
     * show the inner text or not
     */
    private boolean textVisibility = true;
    /**
     * the round rect corner radius
     */
    private int mCornerRadius = 30;
    /**
     * the offset of text padding bottom
     */
    private int mTextPaddingBottomOffset = 5;
    /**
     * moving the text with progress or not
     */
    private boolean isTextMoved = true;

    /**
     * the animator of progress moving
     */
    private AnimatorValue progressAnimator;

    /**
     * the progress of moving
     */
    private float moveProgress = 0;

    /**
     * the paint of drawing progress
     */
    private Paint progressPaint;

    /**
     * the progress of second moving
     */
    private float secondMoveProgress = 0;

    /**
     * the paint of drawing progress
     */
    private Paint secondProgressPaint;
    private Color secondColor;

    /**
     * the gradient of color
     */
    private LinearShader mShader;
    /**
     * the oval's rect shape
     */
    private RectFloat mRect;

    private RectFloat mSecondRect;

    private RectFloat mTrackRect;

    private HorizontalProgressUpdateListener animatorUpdateListener;

    private boolean indeterminate;
    private float indeterminateStart = 0;
    private float indeterminateEnd = 20;
    private int indeterminateGap = 5;
    private RectFloat indeterminateRect;
    private boolean isLoopAnimate;

    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public MaterialHorizontalProgress(Context context) {
        super(context);
        initAttrs(context, null);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrSet 自定义属性
     */
    public MaterialHorizontalProgress(Context context, AttrSet attrSet) {
        super(context, attrSet);
        initAttrs(context, attrSet);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrSet 自定义属性
     * @param styleName 主题名称
     */
    public MaterialHorizontalProgress(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initAttrs(context, attrSet);
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     * @param attrSet 自定义属性
     * @param resId 资源ID
     */
    public MaterialHorizontalProgress(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        initAttrs(context, attrSet);
    }

    private void initAttrs(Context context, AttrSet attrSet) {
        if (attrSet != null) {
            if (attrSet.getAttr("progress").isPresent()) {
                moveProgress = attrSet.getAttr("progress").get().getFloatValue();
            } else {
                moveProgress = 0;
            }
            if (attrSet.getAttr("track_width").isPresent()) {
                mTrackWidth = attrSet.getAttr("track_width").get().getIntegerValue();
            } else {
                mTrackWidth = AttrHelper.vp2px(5, getContext());
            }
            if (attrSet.getAttr("start_progress").isPresent()) {
                mStartProgress = attrSet.getAttr("start_progress").get().getFloatValue();
            } else {
                mStartProgress = 0;
            }
            if (attrSet.getAttr("end_progress").isPresent()) {
                //mEndProgress = attrSet.getAttr("end_progress").get().getFloatValue();
            } else {
                //mEndProgress = 0;
            }
            if (attrSet.getAttr("start_color").isPresent()) {
                mStartColor = attrSet.getAttr("start_color").get().getColorValue();
            } else {
                mStartColor = new Color(Color.getIntColor("#009ad6"));
            }

            if (attrSet.getAttr("end_color").isPresent()) {
                mEndColor = attrSet.getAttr("end_color").get().getColorValue();
            } else {
                mEndColor = new Color(Color.getIntColor("#90d7ec"));
            }
            if (attrSet.getAttr("isTracked").isPresent()) {
                trackEnabled = attrSet.getAttr("isTracked").get().getBoolValue();
            } else {
                trackEnabled = false;
            }

            if (attrSet.getAttr("textColor").isPresent()) {
                mProgressTextColor = attrSet.getAttr("textColor").get().getColorValue();
            } else {
                mProgressTextColor = Color.BLACK;
            }
            if (attrSet.getAttr("textSize").isPresent()) {
                mProgressTextSize = AttrHelper.fp2px(attrSet.getAttr("textSize").get().getIntegerValue(), getContext());
            } else {
                mProgressTextSize = AttrHelper.fp2px(12, getContext());
            }

            if (attrSet.getAttr("animate_type").isPresent()) {
                mAnimateType = attrSet.getAttr("animate_type").get().getIntegerValue();
            } else {
                mAnimateType = AnimatorValue.CurveType.ACCELERATE_DECELERATE;
            }

            if (attrSet.getAttr("track_color").isPresent()) {
                mTrackColor = attrSet.getAttr("track_color").get().getColorValue();
            } else {
                mTrackColor = new Color(Color.getIntColor("#afdfe4"));
            }
            if (attrSet.getAttr("textVisibility").isPresent()) {
                textVisibility = attrSet.getAttr("textVisibility").get().getBoolValue();
            } else {
                textVisibility = false;
            }

            if (attrSet.getAttr("duration").isPresent()) {
                mProgressDuration = attrSet.getAttr("duration").get().getIntegerValue();
            } else {
                mProgressDuration = 1200;
            }
            if (attrSet.getAttr("corner_radius").isPresent()) {
                mCornerRadius = AttrHelper.vp2px(attrSet.getAttr("corner_radius").get().getIntegerValue(), getContext());
            } else {
                mCornerRadius = AttrHelper.vp2px(5, getContext());
            }

            if (attrSet.getAttr("text_padding_bottom").isPresent()) {
                mTextPaddingBottomOffset = AttrHelper.vp2px(attrSet.getAttr("text_padding_bottom").get().getIntegerValue(), getContext());
            } else {
                mTextPaddingBottomOffset = AttrHelper.vp2px(5, getContext());
            }

            if (attrSet.getAttr("text_movedEnable").isPresent()) {
                isTextMoved = attrSet.getAttr("text_movedEnable").get().getBoolValue();
            } else {
                isTextMoved = true;
            }

            if (attrSet.getAttr("second_color").isPresent()) {
                secondColor = attrSet.getAttr("second_color").get().getColorValue();
            } else {
                secondColor = new Color(Color.getIntColor("#90d7ec"));
            }
            if (attrSet.getAttr("second_progress").isPresent()) {
                secondMoveProgress = attrSet.getAttr("second_progress").get().getFloatValue();
            } else {
                secondMoveProgress = 0;
            }

            if (attrSet.getAttr("indeterminate").isPresent()) {
                indeterminate = attrSet.getAttr("indeterminate").get().getBoolValue();
            } else {
                indeterminate = false;
            }

            if (attrSet.getAttr("isLoopAnimate").isPresent()) {
                isLoopAnimate = attrSet.getAttr("isLoopAnimate").get().getBoolValue();
            } else {
                isLoopAnimate = false;
            }
            if (mProgressBackgroundTint != null) {
                mTrackColor = mProgressBackgroundTint;
            }
            if (mSecondaryProgressTint != null) {
                secondColor = mSecondaryProgressTint;
            }
            if (mProgressTint != null) {
                mStartColor = mProgressTint;
                mEndColor = mProgressTint;
            }
        } else {
            initValue();
        }


        setAnimateType(mAnimateType);
        initPaint();
        initListener();
    }

    private void initValue() {
        moveProgress = mStartProgress = 0;
        //mEndProgress = 0;
        secondMoveProgress = 0;
        mStartColor = new Color(Color.getIntColor("#009ad6"));
        mEndColor = new Color(Color.getIntColor("#90d7ec"));
        trackEnabled = false;
        mProgressTextColor = Color.BLACK;
        mProgressTextSize = AttrHelper.fp2px(12, getContext());
        mTrackWidth = AttrHelper.vp2px(5, getContext());
        mAnimateType = AnimatorValue.CurveType.ACCELERATE_DECELERATE;
        mTrackColor = new Color(Color.getIntColor("#afdfe4"));
        textVisibility = false;
        mProgressDuration = 1200;
        mCornerRadius = AttrHelper.vp2px(5, getContext());
        mTextPaddingBottomOffset = AttrHelper.vp2px(5, getContext());
        isTextMoved = true;
        secondColor = new Color(Color.getIntColor("#90d7ec"));
        indeterminate = false;
        isLoopAnimate = false;
    }

    private void initPaint() {
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setStyle(Paint.Style.FILL_STYLE);

        secondProgressPaint = new Paint();
        secondProgressPaint.setAntiAlias(true);
        secondProgressPaint.setStyle(Paint.Style.FILL_STYLE);

    }

    private void initListener() {
        setEstimateSizeListener(new EstimateSizeListener() {
            @Override
            public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
                int width = EstimateSpec.getSize(widthMeasureSpec);
                int height = EstimateSpec.getSize(heightMeasureSpec);
                setEstimatedSize(
                    EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
                    EstimateSpec.getChildSizeWithMode(height, height, EstimateSpec.NOT_EXCEED)
                );

                mShader = new LinearShader(new Point[]{new Point(getPaddingLeft() - 50, (getHeight() - getPaddingTop()) - 50), new Point(getWidth() - getPaddingRight(), (getHeight() / 2F + getPaddingTop() + mTrackWidth))},
                    new float[]{},
                    new Color[]{mStartColor, mEndColor},
                    Shader.TileMode.CLAMP_TILEMODE);
                return true;
            }
        });

        addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                updateTheTrack();
                drawTrack(canvas);
                progressPaint.setShader(mShader, Paint.ShaderType.LINEAR_SHADER);
                if (!indeterminate) {
                    if (secondMoveProgress > 0) {
                        secondProgressPaint.setColor(secondColor);
                        canvas.drawRoundRect(mSecondRect, mCornerRadius, mCornerRadius, secondProgressPaint);
                    }
                    canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, progressPaint);
                    drawProgressText(canvas);
                } else {
                    drawIndeterminate();
                    canvas.drawRoundRect(indeterminateRect, mCornerRadius, mCornerRadius, progressPaint);
                }
            }
        });


        setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                if (isLoopAnimate)
                    startProgressAnimation();
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {

            }
        });
    }

    /**
     * draw the progress text
     *
     * @param canvas mCanvas
     */
    private void drawProgressText(Canvas canvas) {

        if (textVisibility) {
            Paint mTextPaint = new Paint();
            mTextPaint.setStyle(Paint.Style.FILL_STYLE);
            mTextPaint.setTextSize(mProgressTextSize);
            mTextPaint.setColor(mProgressTextColor);
            mTextPaint.setTextAlign(LayoutAlignment.CENTER);

            String progressText = ((int) moveProgress) + "%";
            if (isTextMoved) {
                canvas.drawText(mTextPaint, progressText,
                    (getWidth() - getPaddingLeft() - getPaddingRight()
                        - AttrHelper.vp2px(28, getContext()))
                        * (moveProgress / 100) + AttrHelper.vp2px(10, getContext()),
                    getHeight() / 2F - getPaddingTop() - mTextPaddingBottomOffset);

            } else {
                canvas.drawText(mTextPaint, progressText,
                    (getWidth() - getPaddingLeft()) / 2F,
                    getHeight() / 2F - getPaddingTop() - mTextPaddingBottomOffset);
            }

        }
    }

    /**
     * set progress animate type
     *
     * @param type anim type
     */
    public void setAnimateType(int type) {
        this.mAnimateType = type;
    }

    /**
     * draw the track(moving background)
     *
     * @param canvas mCanvas
     */
    private void drawTrack(Canvas canvas) {
        if (trackEnabled) {
            if (!mShowBackground) {
                return;
            }
            progressPaint.setShader(null, Paint.ShaderType.LINEAR_SHADER);
            progressPaint.setColor(mTrackColor);
            canvas.drawRoundRect(mTrackRect, mCornerRadius, mCornerRadius, progressPaint);
        }
    }

    /**
     * set move progress
     *
     * @param progress progress of moving
     */
    public void setProgress(float progress) {
        this.moveProgress = progress;
        refreshTheView();
    }

    public float getProgress() {
        return this.moveProgress;
    }

    public void setSecondProgress(float progress) {
        if (progress > 0) {

            this.secondMoveProgress = progress;
            refreshTheView();
        }
    }

    public float getSecondProgress() {
        return this.secondMoveProgress;
    }

    public void setSecondColor(Color color) {
        this.secondColor = color;
    }

    public void setIndeterminate(boolean bool) {
        this.indeterminate = bool;
    }

    /**
     * set start progress
     *
     * @param startProgress start progress
     */
    public void setStartProgress(float startProgress) {
        if (startProgress < 0 || startProgress > 100) {
            throw new IllegalArgumentException("Illegal progress value, please change it!");
        }
        this.mStartProgress = startProgress;
        this.moveProgress = mStartProgress;
        refreshTheView();
    }

    /**
     * set end progress
     *
     * @param endProgress end progress
     */
    public void setEndProgress(float endProgress) {
        if (endProgress < 0 || endProgress > 100) {
            throw new IllegalArgumentException("Illegal progress value, please change it!");
        }
        //this.mEndProgress = endProgress;
        refreshTheView();

    }

    /**
     * set start color
     *
     * @param startColor start point color
     */
    public void setStartColor(Color startColor) {
        this.mStartColor = startColor;
        mShader = new LinearShader(new Point[]{new Point(getPaddingLeft() - 50,
            (getHeight() - getPaddingTop()) - 50),
            new Point(getWidth() - getPaddingRight(),
                getHeight() / 2F + getPaddingTop() + mTrackWidth)},
            new float[]{},
            new Color[]{mStartColor, mEndColor},
            Shader.TileMode.CLAMP_TILEMODE);
        refreshTheView();
    }

    /**
     * set end color
     *
     * @param endColor end point color
     */
    public void setEndColor(Color endColor) {
        this.mEndColor = endColor;
        mShader = new LinearShader(new Point[]{new Point(getPaddingLeft() - 50,
            (getHeight() - getPaddingTop()) - 50),
            new Point(getWidth() - getPaddingRight(),
                getHeight() / 2F + getPaddingTop() + mTrackWidth)},
            new float[]{},
            new Color[]{mStartColor, mEndColor},
            Shader.TileMode.CLAMP_TILEMODE);
        refreshTheView();
    }

    /**
     * set the width of progress stroke
     *
     * @param width stroke
     */
    public void setTrackWidth(int width) {
        this.mTrackWidth = AttrHelper.vp2px(width, getContext());
        refreshTheView();
    }

    /**
     * set track color for progress background
     *
     * @param color bg color
     */
    public void setTrackColor(Color color) {
        if (!mShowBackground) {
            return;
        }
        this.mTrackColor = color;
        refreshTheView();
    }

    /**
     * set text color for progress text
     *
     * @param textColor
     */
    public void setProgressTextColor(Color textColor) {
        this.mProgressTextColor = textColor;
    }

    /**
     * set text size for inner text
     *
     * @param size text size
     */
    public void setProgressTextSize(int size) {
        mProgressTextSize = AttrHelper.fp2px(size, getContext());
        refreshTheView();
    }

    /**
     * set duration of progress moving
     *
     * @param duration
     */
    public void setProgressDuration(int duration) {
        this.mProgressDuration = duration;
    }

    /**
     * set track for progress
     *
     * @param trackAble whether track or not
     */
    public void setTrackEnabled(boolean trackAble) {
        this.trackEnabled = trackAble;
        refreshTheView();
    }

    /**
     * set the visibility for progress inner text
     *
     * @param visibility text visible or not
     */
    public void setProgressTextVisibility(boolean visibility) {
        this.textVisibility = visibility;
        refreshTheView();
    }

    /**
     * set progress text moving with progress view or not
     *
     * @param moved
     */
    public void setProgressTextMoved(boolean moved) {
        this.isTextMoved = moved;
    }

    public void setIsLoopAinimate(boolean isLoopAnimate) {
        this.isLoopAnimate = isLoopAnimate;
    }

    /**
     * start the progress's moving
     */
    public void startProgressAnimation() {
        progressAnimator = new AnimatorValue();
        progressAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float progress) {
                if (animatorUpdateListener != null) {
                    animatorUpdateListener.onHorizontalProgressUpdate(MaterialHorizontalProgress.this, progress);
                }

                if (!indeterminate) {
                    moveProgress = progress * 100;
                } else {
                    if ((progress * 100) < 10) {
                        indeterminateGap = 5;
                    }
                    indeterminateStart = (progress * 100);
                    indeterminateEnd = (progress * 100) + indeterminateGap;
                    indeterminateGap += 10;
                }
                invalidate();
            }
        });
        progressAnimator.setCurveType(mAnimateType);
        progressAnimator.setDuration(mProgressDuration);


        progressAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                if (animatorUpdateListener != null) {
                    animatorUpdateListener.onHorizontalProgressStart(MaterialHorizontalProgress.this);
                }
            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                if (animatorUpdateListener != null) {
                    animatorUpdateListener.onHorizontalProgressFinished(MaterialHorizontalProgress.this);
                }
                if (isLoopAnimate) {
                    progressAnimator.start();
                }
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        progressAnimator.start();
    }


    /**
     * stop the progress moving
     */
    public void stopProgressAnimation() {
        if (progressAnimator != null) {
            isLoopAnimate = false;
            progressAnimator.cancel();
            progressAnimator = null;
        }
    }

    /**
     * set the corner radius for the rect of progress
     *
     * @param radius the corner radius
     */
    public void setProgressCornerRadius(int radius) {
        this.mCornerRadius = AttrHelper.vp2px(radius, getContext());
        refreshTheView();
    }

    /**
     * set the text padding bottom offset
     *
     * @param offset the value of padding bottom
     */
    public void setProgressTextPaddingBottom(int offset) {
        this.mTextPaddingBottomOffset = AttrHelper.vp2px(offset, getContext());
    }


    /**
     * refresh the layout
     */
    private void refreshTheView() {
        invalidate();
        //requestLayout();
    }

    /**
     * update the oval progress track
     */
    private void updateTheTrack() {
        mRect = new RectFloat(getPaddingLeft() + mStartProgress * (getWidth() - getPaddingLeft() - getPaddingRight() + 60) / 100,
            getHeight() / 2F - getPaddingTop(),
            (getWidth() - getPaddingRight() - 20) * ((moveProgress) / 100),
            getHeight() / 2F + getPaddingTop() + mTrackWidth);
        mTrackRect = new RectFloat(getPaddingLeft(), getHeight() / 2F - getPaddingTop(),
            (getWidth() - getPaddingRight() - 20),
            getHeight() / 2F + getPaddingTop() + mTrackWidth);

        mSecondRect = new RectFloat(getPaddingLeft() + mStartProgress * (getWidth() - getPaddingLeft() - getPaddingRight() + 60) / 100,
            getHeight() / 2F - getPaddingTop(),
            (getWidth() - getPaddingRight() - 20) * ((secondMoveProgress) / 100),
            getHeight() / 2F + getPaddingTop() + mTrackWidth);
    }

    private void drawIndeterminate() {
        indeterminateRect = new RectFloat(getPaddingLeft() + indeterminateStart * (getWidth() - getPaddingLeft() - getPaddingRight() + 60) / 100,
            getHeight() / 2F - getPaddingTop(),
            (getWidth() - getPaddingRight() - 20) * ((indeterminateEnd) / 100),
            getHeight() / 2F + getPaddingTop() + mTrackWidth);
    }

    /**
     * 进度条更新监听
     */
    public interface HorizontalProgressUpdateListener {
        /**
         * 进度条开始更新
         *
         * @param component
         */
        void onHorizontalProgressStart(Component component);

        /**
         * 进度条更新中
         *
         * @param component
         * @param progress
         */
        void onHorizontalProgressUpdate(Component component, float progress);

        /**
         * 进度条更新结束
         *
         * @param component
         */
        void onHorizontalProgressFinished(Component component);

    }

    /**
     * set the progress update listener for progress view
     *
     * @param listener update listener
     */
    public void setProgressViewUpdateListener(HorizontalProgressUpdateListener listener) {
        this.animatorUpdateListener = listener;
    }
}
